/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import org.springframework.core.NamedThreadLocal;
import org.springframework.lang.Nullable;

/**
 * 类, 该类包含用于获取有关当前AOP调用的信息的static方法.
 *
 * <p>如果AOP框架被配置为公开当前代理(而不是默认代理), {@code currentProxy()}方法是可用的.
 * 它返回正在使用的AOP代理. target对象或advice可以使用它来进行advised调用, 就像在ejb中使用
 * {@code getEJBObject()}一样. 他们还可以使用它来查找advice配置.
 *
 * <p>Spring的AOP框架默认情况下不公开代理, 因为这样做会带来性能成本.
 *
 * <p>该类中的功能可能由需要访问invocation上的资源的target对象使用.
 * 然而, 当有合理的替代方案时, 不应该使用这种方法, 因为它使应用程序代码依赖于AOP(特别是Spring AOP框架)下的使用.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 13.03.2003
 */
public final class AopContext {

	/**
	 * 与此线程关联的AOP代理的ThreadLocal holder.
	 * 将包含{@code null}, 除非控制代理配置上的"exposeProxy"属性被设置为"true".
	 * @see ProxyConfig#setExposeProxy
	 */
	private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");


	private AopContext() {
	}


	/**
	 * 尝试返回当前的AOP代理. 只有当调用方法是通过AOP调用的, 并且AOP框架被设置为公开代理时,
	 * 这个方法才可用. 否则, 该方法将抛出IllegalStateException异常.
	 * @return 当前AOP代理(永远不会返回{@code null})
	 * @throws IllegalStateException 如果找不到proxy, 因为方法是在AOP invocation context
	 * 之外调用的, 或者因为AOP框架没有配置为公开proxy
	 */
	public static Object currentProxy() throws IllegalStateException {
		Object proxy = currentProxy.get();
		if (proxy == null) {
			throw new IllegalStateException(
					"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and " +
							"ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.");
		}
		return proxy;
	}

	/**
	 * 通过{@code currentProxy()}方法使给定的proxy可用.
	 * <p>请注意, 调用者应该小心地保留旧值.
	 * @param proxy 要公开的proxy(或{@code null}来重置它)
	 * @return 旧的proxy, 如果没有绑定, 它可能是{@code null}
	 * @see #currentProxy()
	 */
	@Nullable
	static Object setCurrentProxy(@Nullable Object proxy) {
		Object old = currentProxy.get();
		if (proxy != null) {
			currentProxy.set(proxy);
		}
		else {
			currentProxy.remove();
		}
		return old;
	}

}
